package controller;

import java.util.ArrayList;

import dal.JMLDatabase;
import dal.datastore.TableData;
import dto.ColumnDTO;
import dto.QueryParserDTO;
import dto.QueryResultDTO;
import dto.RowDTO;

public class QueryController {
	private String _query;
	private QueryParser _parser;

	public QueryController(String _query) {
		super();
		this._query = _query;
		this._parser = new QueryParser(this._query);
	}

	@SuppressWarnings("unchecked")
	public QueryResultDTO executeQuery() throws Exception {
		QueryParserDTO info = this._parser.parse();
		if (info == null) {
			return null;
		}

		EQueryType type = info.getType();
		DatabaseController dbCtrl;
		TableController tblCtrl;

		switch (type) {
		case CREATE_DATABSE:
			System.out.println(this._query);
			// create database controller with dbname
			dbCtrl = new DatabaseController(info.getData().get(0).toString());
			dbCtrl.createDatabase();
			return new QueryResultDTO(EQueryType.CREATE_DATABSE, null);
		case USE_DATABASE:
			ControllerConfiguration.DEFAULT_DATABASE = info.getData().get(0)
					.toString();
			return new QueryResultDTO(EQueryType.USE_DATABASE, new TableData(
					ControllerConfiguration.DEFAULT_DATABASE));
		case CREATE_TABLE:
			System.out.println(this._query);
			String tableName = info.getData().get(0).toString();

			@SuppressWarnings("unchecked")
			ArrayList<ColumnDTO> columns = (ArrayList<ColumnDTO>) info
					.getData().get(1);
			if (tableName == null || columns == null) {
				throw new Exception("Invalid table name");
			}

			if (ControllerConfiguration.DEFAULT_DATABASE.isEmpty()) {
				throw new Exception("Default database is not set yet!");
			}
			tblCtrl = new TableController(
					ControllerConfiguration.DEFAULT_DATABASE, tableName,
					columns);
			if (!tblCtrl.createTable()) {
				throw new Exception("Invalid table name");
			}
			return new QueryResultDTO(EQueryType.CREATE_TABLE, null);
		case DROP_DATABASE:
			System.out.println(this._query);
			// Drop database
			dbCtrl = new DatabaseController(info.getData().get(0).toString());
			// If drop failed.
			if (!dbCtrl.dropDatabase()) {
				throw new Exception("Database name is invalid");
			}
			return new QueryResultDTO(EQueryType.DROP_DATABASE, null);
		case DROP_TABLE:
			System.out.println(this._query);
			if (ControllerConfiguration.DEFAULT_DATABASE.isEmpty()) {
				throw new Exception("Default database is not set yet!");
			}
			// Drop database
			tblCtrl = new TableController(
					ControllerConfiguration.DEFAULT_DATABASE, info.getData()
							.get(0).toString());
			// If drop failed.
			if (!tblCtrl.dropTable()) {
				throw new Exception("Table name is invalid");
			}
			return new QueryResultDTO(EQueryType.DROP_TABLE, null);
		case INSERT:
			if (ControllerConfiguration.DEFAULT_DATABASE.isEmpty()) {
				throw new Exception("Default database is not set yet!");
			}
			// 1. Get table name
			String tblName = (String) info.getData().get(0);
			tblCtrl = new TableController(
					ControllerConfiguration.DEFAULT_DATABASE, tblName);

			ArrayList<String> cols;
			ArrayList<Object> values;
			if (info.getData().size() == 2) {
				values = (ArrayList<Object>) info.getData().get(1);
			} else {
				cols = (ArrayList<String>) info.getData().get(1);
				values = (ArrayList<Object>) info.getData().get(2);

				// Validate columns
				ArrayList<ColumnDTO> colDTOs = tblCtrl.getTableColumns();
				if (colDTOs == null) {
					throw new Exception("Table '" + tblName + "' is not exists");
				} else {
					if (cols.size() != colDTOs.size()
							|| cols.size() != values.size()) {
						throw new Exception("Number of columns is invalid");
					} else {
						for (int i = 0; i < cols.size(); i++) {
							if (!cols.get(i).equalsIgnoreCase(
									colDTOs.get(i).getName())) {
								throw new Exception("Columns name is not valid");
							}
						}
					}
				}
			}

			// 2. Insert data
			ArrayList<RowDTO> rows = new ArrayList<RowDTO>();
			RowDTO row = new RowDTO(values);
			rows.add(row);
			tblCtrl.insertRows(rows);

			return new QueryResultDTO(EQueryType.INSERT, null);
		case SELECT:
			ArrayList<Object> data = info.getData();
			int queryType = data.size();
			String select = (String) data.get(0);
			String from = (String) data.get(1);
			String where = "";
			if (queryType == 3) {
				where = (String) data.get(2);
			}
			FROMCommand fromCmd = new FROMCommand(from);
			TableData tblData = fromCmd.excute();
			if (!where.isEmpty()) {
				WHERECommand whereCmd = new WHERECommand(where, tblData);
				tblData = whereCmd.execute(null);
			}
			SELECTCommand selectCmd = new SELECTCommand(select, tblData);
			TableData result = selectCmd.execute();

			return new QueryResultDTO(EQueryType.SELECT, result);
		case UPDATE:
			data = info.getData();
			queryType = data.size();
			String update = (String) data.get(0);
			String set = (String) data.get(1);
			where = "";
			if (queryType == 3) {
				where = (String) data.get(2);
			}

			fromCmd = new FROMCommand(update);
			tblData = fromCmd.excute();

			ArrayList<Integer> indexes = new ArrayList<Integer>();
			if (!where.isEmpty()) {
				WHERECommand whereCmd = new WHERECommand(where, tblData);
				tblData = whereCmd.execute(indexes);
			}
			UPDATECommand updateCmd = new UPDATECommand(set, tblData);
			tblData = updateCmd.execute(indexes);
			if (tblData != null) {
				JMLDatabase dao = new JMLDatabase();
				boolean isSucceed = dao.updateTableData(
						ControllerConfiguration.DEFAULT_DATABASE, update,
						indexes, tblData.getRows());

			}
			return new QueryResultDTO(EQueryType.UPDATE, null);
		case DELETE:
			data = info.getData();
			queryType = data.size();
			String delete = (String) data.get(0);
			where = "";
			if (queryType == 2) {
				where = (String) data.get(1);
			}

			fromCmd = new FROMCommand(delete);
			tblData = fromCmd.excute();

			indexes = new ArrayList<Integer>();
			if (!where.isEmpty()) {
				WHERECommand whereCmd = new WHERECommand(where, tblData);
				tblData = whereCmd.execute(indexes);
			}
			
			if (tblData != null) {
				JMLDatabase dao = new JMLDatabase();
				boolean isSucceed = dao.deleteTableData(
						ControllerConfiguration.DEFAULT_DATABASE, delete,
						indexes);

			}
			return new QueryResultDTO(EQueryType.UPDATE, null);
		default:
			break;
		}
		return null;
	}
}
